# 导入必要的库
vec_values = [0] * 9
import plotly.graph_objects as go
from dash import dcc, html, Input, Output, State
import numpy as np
import dash

# 定义获取向量组件的函数
# 返回一个包含向量输入框、按钮和绘图区域的HTML布局
# 用于用户输入向量并进行相关操作
def get_vectors_component():
    return html.Div([
        html.H3('三维向量可视化'),
        html.Div([
            html.Label('向量1:'),
            dcc.Input(id='vector1-x', type='number', placeholder='X', value=0),
            dcc.Input(id='vector1-y', type='number', placeholder='Y', value=0),
            dcc.Input(id='vector1-z', type='number', placeholder='Z', value=0),
        ]),
        html.Div([
            html.Label('向量2:'),
            dcc.Input(id='vector2-x', type='number', placeholder='X', value=0),
            dcc.Input(id='vector2-y', type='number', placeholder='Y', value=0),
            dcc.Input(id='vector2-z', type='number', placeholder='Z', value=0),
        ]),
        html.Div([
            html.Label('向量3:'),
            dcc.Input(id='vector3-x', type='number', placeholder='X', value=0),
            dcc.Input(id='vector3-y', type='number', placeholder='Y', value=0),
            dcc.Input(id='vector3-z', type='number', placeholder='Z', value=0),
        ]),
        html.Button('绘制向量', id='plot-button', n_clicks=0),
        html.Button('全0向量', id='all-zero-button', n_clicks=0),
        html.Button('全1向量', id='all-one-button', n_clicks=0),
        html.Button('随机向量', id='random-button', n_clicks=0),
        html.Button('正交向量', id='orthogonal-button', n_clicks=0),
        dcc.Graph(id='vector-plot', style={'width': '1024px', 'height': '768px'}),
        html.Div(id='calculation-result')
    ])

# 注册向量相关的回调函数
# 处理按钮点击事件，更新绘图和计算结果
# 并根据用户操作更新向量输入框的值
def register_vectors_callbacks(app):
    @app.callback(
        Output('vector-plot', 'figure'),
        Output('calculation-result', 'children'),
        Output('vector1-x', 'value'),
        Output('vector1-y', 'value'),
        Output('vector1-z', 'value'),
        Output('vector2-x', 'value'),
        Output('vector2-y', 'value'),
        Output('vector2-z', 'value'),
        Output('vector3-x', 'value'),
        Output('vector3-y', 'value'),
        Output('vector3-z', 'value'),
        Input('plot-button', 'n_clicks'),
        Input('all-zero-button', 'n_clicks'),
        Input('all-one-button', 'n_clicks'),
        Input('random-button', 'n_clicks'),
        Input('orthogonal-button', 'n_clicks'),
        State('vector1-x', 'value'),
        State('vector1-y', 'value'),
        State('vector1-z', 'value'),
        State('vector2-x', 'value'),
        State('vector2-y', 'value'),
        State('vector2-z', 'value'),
        State('vector3-x', 'value'),
        State('vector3-y', 'value'),
        State('vector3-z', 'value')
    )
    # 更新绘图和计算结果的函数
    # 根据按钮点击事件和向量输入值进行相应的操作
    def update_plot(n_clicks_plot, n_clicks_zero, n_clicks_one, n_clicks_random, *input_vec_values):
        global vec_values
        # 获取回调上下文，确定触发事件的按钮
        ctx = dash.callback_context
        if not ctx.triggered:
            button_id = None
        else:
            button_id = ctx.triggered[0]['prop_id'].split('.')[0]
        # 输入框值变更时更新全局vec_values
        #import pdb; pdb.set_trace()
        vec_values = list(input_vec_values)[-9:]
        # 若未点击按钮或向量值为空，返回初始状态
        if any(val is None for val in input_vec_values):
            vec_values = [0] * 9
            return go.Figure(), html.Div(""), 0, 0, 0, 0, 0, 0, 0, 0, 0
        # 封装绘图和计算结果的函数
        def plot_and_calculate(vec_values):
            fig = go.Figure()
            vectors = []
            for i in range(0, len(vec_values), 3):
                if i + 2 < len(vec_values):
                    vec = np.array([vec_values[i], vec_values[i + 1], vec_values[i + 2]])
                    vectors.append(vec)
            for i, vec in enumerate(vectors, start=1):
                fig.add_trace(go.Scatter3d(
                    x=[0, vec[0]], y=[0, vec[1]], z=[0, vec[2]],
                    mode='lines+markers',
                    name=f'向量{i}'
                ))
            dot_products = []
            cross_products = []
            for i in range(len(vectors)):
                for j in range(i + 1, len(vectors)):
                    dot_products.append(np.dot(vectors[i], vectors[j]))
                    cross_products.append(np.cross(vectors[i], vectors[j]))
            result_children = []
            for i, dot in enumerate(dot_products):
                result_children.append(html.P(f'向量{i+1}和向量{i+2}点积结果: {dot}'))
            for i, cross in enumerate(cross_products):
                result_children.append(html.P(f'向量{i+1}和向量{i+2}叉积结果: {cross}'))
            
            # 计算向量长度
            vector_lengths = [np.linalg.norm(vec) for vec in vectors]
            for i, length in enumerate(vector_lengths, start=1):
                result_children.append(html.P(f'向量{i}的长度: {length:.4f}'))
                
            # 计算向量夹角
            if len(vectors) >= 2:
                for i in range(len(vectors)):
                    for j in range(i+1, len(vectors)):
                        a = vectors[i]
                        b = vectors[j]
                        cos_theta = np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
                        angle = np.arccos(np.clip(cos_theta, -1.0, 1.0))
                        angle_deg = np.degrees(angle)
                        result_children.append(html.P(f'向量{i+1}和向量{j+1}的夹角: {angle_deg:.2f}°'))
            
            return fig, result_children, vec_values[:9]
        # 若点击全0向量按钮，将向量值设为全0
        if button_id == 'all-zero-button':
            vec_values = [0] * 9
            fig, result_children, _ = plot_and_calculate(vec_values)
            return fig, html.Div(result_children), *vec_values[:9]
        # 若点击全1向量按钮，将向量值设为全1
        elif button_id == 'all-one-button':
            vec_values = [1] * 9
            fig, result_children, _ = plot_and_calculate(vec_values)
            return fig, html.Div(result_children), *vec_values[:9]
        # 若点击随机生成向量按钮，生成随机向量值
        elif button_id == 'random-button':
            vec_values = np.random.rand(9).tolist()
            print(f"input_vec_values: {vec_values}")
            fig, result_children, _ = plot_and_calculate(vec_values)
            return fig, html.Div(result_children), *vec_values[:9]
        # 若点击绘制向量按钮，使用当前向量值
        elif button_id == 'plot-button':
            vec_values = list(input_vec_values)[-9:]
            fig, result_children, _ = plot_and_calculate(vec_values)
            return fig, html.Div(result_children), *vec_values[:9]
        # 若点击正交向量按钮，生成正交向量值
        if button_id == 'orthogonal-button':
            vectors = []
            for i in range(0, len(vec_values), 3):
                if i + 2 < len(vec_values):
                    vec = np.array([vec_values[i], vec_values[i + 1], vec_values[i + 2]])
                    vectors.append(vec)
            # 使用Gram - Schmidt正交化方法生成三条正交向量
            if len(vectors) >= 3:
                u1 = vectors[0]
                if np.linalg.norm(u1) > 0:
                    u1 = u1 / np.linalg.norm(u1)
                else:
                    u1 = np.array([1, 0, 0])
                v2 = vectors[1] - np.dot(vectors[1], u1) * u1
                if np.linalg.norm(v2) > 0:
                    u2 = v2 / np.linalg.norm(v2)
                else:
                    if u1[0] != 0:
                        u2 = np.array([-u1[1]/u1[0], 1, 0])
                    elif u1[1] != 0:
                        u2 = np.array([1, -u1[0]/u1[1], 0])
                    else:
                        u2 = np.array([1, 0, 0])
                    u2 = u2 / np.linalg.norm(u2)
                v3 = vectors[2] - np.dot(vectors[2], u1) * u1 - np.dot(vectors[2], u2) * u2
                if np.linalg.norm(v3) > 0:
                    u3 = v3 / np.linalg.norm(v3)
                else:
                    u3 = np.cross(u1, u2)
                    if np.linalg.norm(u3) > 0:
                        u3 = u3 / np.linalg.norm(u3)
                    else:
                        u3 = np.array([0, 0, 1])
                vec_values = [*u1, *u2, *u3]
            fig, result_children, _ = plot_and_calculate(vec_values)
            return fig, html.Div(result_children), *vec_values[:9]
        # 返回绘图对象, 计算结果和更新后的向量值
        result_children = []
        return go.Figure(), html.Div(result_children), *vec_values[:9]